<?php
/**
 * Created by PhpStorm.
 * User: pc_usr
 * Date: 2017/10/27
 * Time: 16:22
 */


/**
 * redis连接实例
 */
function redis_con()
{
    global $config;
    
    $redis = new redis();
//     $redis->pconnect($config['REDIS_SERVER'], $config['REDIS_PORT']);
    $redis->connect($config['REDIS_SERVER'], $config['REDIS_PORT']);
    if ($config['REDIS_PWD']) {
        $redis->auth($config['REDIS_PWD']);
    }
    return $redis;
}


/**
 * CURL
 * @param $url 请求地址
 * @param $Async 异步超时毫秒
 * @param $gzip 是否需要gzip解压
 * @param string $type 请求类型，GET或POST
 * @param string $data 请求内容
 * @return mixed|string
 */
function curl($url, $Async = 500, $gzip = false, $type = 'GET', $data = '')
{
    //$Asynchronous：异步请求
    $ch = curl_init();
    if ($Async) {
        curl_setopt($ch, CURLOPT_NOSIGNAL, true);//注意，毫秒超时一定要设置这个
        curl_setopt($ch, CURLOPT_TIMEOUT_MS, $Async);//超时毫秒，小于500时不稳定，测试在600以上可以
    }
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
    curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (compatible; MSIE 5.01; Windows NT 5.0)');
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
    curl_setopt($ch, CURLOPT_AUTOREFERER, 1);
    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $type);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    if ($gzip) curl_setopt($ch, CURLOPT_ENCODING, "gzip"); // 关键在这里
    $info = curl_exec($ch);
    if (curl_errno($ch)) {
        return 'Errno' . curl_error($ch);
    }
    curl_close($ch);
    return $info;
}


/**
 * 初始化
 */
function init()
{
    global $redis, $prev, $u2, $analyz;
    
    // 初始化，导入最近20期
    $res = curl($u2, 8000, true);
    $res = json_decode($res, true);
    $res = array_reverse($res['awardNumberInfoList']); // 取出并反转
    
    $v = array();
    $i = 0;
    foreach ($res as $value) {
        $v[0] = $value['period'];
        $v[1] = $value['winningNumber'];
        $t = $redis->lGetRange($prev . 'ssc', 0, 0);
        if ($t) {
            $t = json_decode($t[0], true);
        }
        // 验证期数
        if (!$t || $t[0] < $v[0]) {
            
            // 验证开奖结果
            if (preg_match_all("/[0-9]/", $v[1]) == 5) {
                
                $redis->lPush($prev . 'ssc', json_encode($v)); // 保存到缓存list
                $i += 1;
            }
        }
    }
    echo "\n初始化导入完成，共导入{$i}期，最近一期：{$v[0]}\n\n";
    
    // 生成分析数据
    $tmp = $redis->lGetRange($prev . 'ssc', 0, 119); // 取出缓存中最近120条
    $tmp = array_reverse($tmp); // 反转
    $redis->del($prev . 'analyz'); // 清除旧的分析数据
    foreach ($tmp as $k => $v) {
        
        $v = json_decode($v);
        $analyz->send(['period' => $v[0], 'num' => $v[1]]); // 分析
        $re = $analyz->current(); // 获取分析结果
        $analyz->next(); // 前进
        up_cache($re); // 更新分析结果的缓存
    }
}

/**
 * 初始化
 */
function init_kl8()
{
    get_current_kl8();
}


/**
 * 获取开奖结果
 * @param $period 期号
 */
function get_result($period)
{
    global $redis, $prev, $u3, $analyz;
    
    do {
        
        echo "获取当期开奖……\n";
        $url = $u3 . $period;
        $re = curl($url, 5000);
        $re = json_decode($re);
        
        if (isset($re->status) && $re->status == 0) {
            
            $re = $re->awardNumberInfoList[0];
            $v[0] = $re->period;
            $v[1] = $re->winningNumber;
            
            // 验证期号和开奖结果
            if ($v[0] && preg_match_all("/[0-9]/", $v[1]) == 5) {
                
                // 保存到缓存中，供订单处理平仓
                $redis->hSet($prev . 'current', 'ssc_openeriod', $v[0]); // 开奖期号
                $redis->hSet($prev . 'current', 'ssc_winningNumber', $v[1]); // 开奖结果
                
                //写入存储队列
                $redis->lPush($prev . 'win_queue', json_encode([$v[0], $v[1]]));
                $redis->lPush($prev . 'win_oqueue', json_encode([
                    'type'=>'ssc',
                    'data'=>[$v[0], $v[1]]
                ]));
                
                //开奖结果写入缓存
                $redis->setex($prev . 'winresult' . $v[0], 3600 * 24 * 30, $v[1]);
                
                // 读取缓存中的最近一期P
                $t = $redis->lGetRange($prev . 'ssc', 0, 0);
                if ($t) {
                    $t = json_decode($t[0], true);
                }
                // 对比缓存期数
                if (!$t || $t[0] < $v[0]) {
                    
                    // 加入缓存list
                    $redis->lPush($prev . 'ssc', json_encode($v));
                    
                    //删除过多的缓存数据
                    if($redis->lSize($prev . 'ssc') > 120){
                        $redis->rPop($prev . 'ssc');
                    }
                    
                    echo "当期获取成功：{$re->period}\n";
                    
                    // 分析趋势
                    $analyz->send(['period' => $v[0], 'num' => $v[1]]); // 分析
                    $re = $analyz->current(); // 获取分析结果
                    $analyz->next(); // 前进
                    up_cache($re); // 更新分析结果的缓存
                    
                    get_current(); // 递归，获取当期信息
                    $again = false; // 结束循环
                }
                
            } else {
                
                // 如果获取失败，延时几秒后重新获取
                echo "当期获取失败，等待重新获取：{$period}\n";
                sleep(3);
                $again = true;
            }
            
        } else {
            
            // 如果获取失败，延时几秒后重新获取
            echo "当期获取失败，等待重新获取：{$period}\n";
            sleep(3);
            $again = true;
        }
        
    } while ($again);
}

/**
 * 获取开奖结果
 * _current  -- hash structure
 *
 * kl8_Issue 快乐8上期开奖期号
 * kl8_WinNumber 快乐8上期开奖结果
 * kl8 快乐8本期下单期号
 * kl8_openTime 快乐8本期即将开奖时间
 *
 * {"0":{"Issue":"853826","WinNumber":"09 11 18 24 31 32 39 47 51 54 56 58 60 61 63 65 67 68 74 76+03","EndTime":"2017-11-01 16:50:00"},"preIssue":"853826","preEndTime":"1509526200","now":"1509526334","openTime":"1509526500"}
 *
 *
 * issue 和 preissue可能不同 当两者相同时，获取开奖号码，否则可以10秒后获取
 *
 *  Issue      上一期期号
 *  WinNumber  上一期开奖号码
 *  EndTime    上一期结期结束时间
 * preIssue    上一期期号
 * preEndTime  上一期结束时间
 * now       当前时间
 * openTime  本期将要开奖时间
 *
 */
function get_result_kl8()
{
    global $redis, $prev, $u3_kl8, $analyz_kl8;
    
    do {
        echo "获取当期开奖……\n";
        $url = $u3_kl8;
        $re = curl($url, 5000);
        $re = json_decode($re, true);
        
        $v[0] = $re[0]['Issue']; // 上期开奖期号
        $v[1] = $re[0]['WinNumber']; // 上期开奖结果
        $v[2] = $re['preEndTime']; // 上期开奖时间
        
        if(isset($re['now']) && $re['preIssue'] == $re[0]['Issue']){
            
            // 保存到缓存中，供订单处理平仓
            $redis->hSet($prev . 'current', 'kl8_Issue', $v[0]); // 开奖期号
            $redis->hSet($prev . 'current', 'kl8_WinNumber', $v[1]); // 开奖结果
            //$redis->hSet($prev . 'current', 'kl8_openTime', $v[2]); // 设置开奖时间
            
            //写入存储队列
            $redis->lPush($prev . 'win_queue', json_encode([$v[0], $v[1]]));
            $redis->lPush($prev . 'win_oqueue', json_encode([
                'type'=>'kl8',
                'data'=>[$v[0], $v[1]]
            ]));
            
            //开奖结果写入缓存
            $redis->setex($prev . 'winresult' . $v[0], 3600 * 24 * 30, $v[1]);
            
            
            //now
            $send_k = false;
            if($redis->exists($prev . 'kl8') && $redis->lSize($prev . 'kl8') >= 1){
                $last_issue = $redis->lrange($prev . 'kl8', 0, 0);
                $last_issue = json_decode($last_issue[0], true);
                
                if($last_issue[0] != $re[0]['Issue']){
                    // 加入缓存list
                    $redis->lPush($prev . 'kl8', json_encode($v));
                    $send_k = true;
                }
            }else{
                // 加入缓存list
                $redis->lPush($prev . 'kl8', json_encode($v));
                $send_k = true;
            }
            
            //删除过多的缓存数据
            if($redis->lSize($prev . 'kl8') > 120){
                $redis->rPop($prev . 'kl8');
            }
            
            //             if($send_k){
            if(true){
                // 分析趋势
                $analyz_kl8->send(['period' => $v[0], 'num' => $v[1], 'open_time' => $re['preEndTime']]); // 分析
                $rek = $analyz_kl8->current(); // 获取分析结果
                
                $analyz_kl8->next(); // 前进
                up_cache_kl8($rek); // 更新分析结果的缓存
            }
            
            get_current_kl8(); // 递归，获取当期信息
            $again = false; // 结束循环
        }else{
            // 如果获取失败，延时几秒后重新获取
            echo "当期获取失败，等待重新获取\n";
            sleep(3);
            $again = true;
        }
        
    } while ($again);
}


/**
 * 获取当期信息
 */
function get_current()
{
    global $u1, $redis, $prev;
    
    do {
        
        echo "\n获取当期信息……\n";
        $res = curl($u1, 5000);
        $res = json_decode($res);
        
        if (isset($res->status) && $res->status == 0) {
            
            echo "上一期：{$res->previousPeriod}\n";
            echo "当期：{$res->currentPeriod}\n";
            echo "下一期：{$res->nextPeriod}\n";
            $secondsLeft = $res->secondsLeft / 1000 / 60; // 当期剩余时间
            echo "\n当期剩余时间：{$secondsLeft}\n\n";
            
            // 延时获取开奖信息
            $period = $res->currentPeriod; // 当前期号
            $delay = $res->secondsLeft + 1000 * 130; // 延迟时间，毫秒
            swoole_timer_after($delay, function() use ($period){ // 延时执行
                
                get_result($period); // 获取开奖信息
            });
                
                // 设置开奖时间，供订单处理用
                $ssc_opentime = $redis->hGet($prev . 'current', 'ssc_opentime');
                if (! $ssc_opentime) { // 在每次下单截止后，会更新当期下单期号，并清空开奖时间
                    
                    $c_ssc =  $redis->hGet($prev . 'current', 'ssc');
                    if ($c_ssc && $period != $c_ssc) { // 验证期号是否一致
                        // 如果如果期号不同，可能定时器混乱
                        echo "\n当期信息错误！period|$period||c_ssc|$c_ssc\n";
                        
                    } else {
                        
                        // 订单处理平仓时间，在开奖时间上，再加若干秒，视情况调整
                        $openTime = time() + round($delay / 1000 + 10);
                        $redis->hSet($prev . 'current', 'ssc_openTime', $openTime); // 设置开奖时间
                    }
                }
                
                // 当期停止下单后，修改当期编号，供下单使用
                $nextPeriod = $res->nextPeriod; // 下一期
                swoole_timer_after($res->secondsLeft, function() use ($nextPeriod, $redis, $prev){ // 延时执行
                    
                    $redis->hSet($prev . 'current', 'ssc', $nextPeriod); // 更新当期编号
                    $redis->hSet($prev . 'current', 'ssc_openTime', ''); // 清除开奖时间
                });
                    
                    $again = false; // 结束循环
                    
        } else {
            
            // 如果获取失败，延时几秒后重新获取
            sleep(3);
            $again = true;
        }
        
    } while ($again);
}

/**
 * 获取当期信息
 * 正在下注中
 *
 * opened
 * {"0":{"Issue":"853819","WinNumber":"06 07 14 21 29 32 33 34 38 41 42 47 54 59 61 65 67 74 76 80+01","EndTime":"2017-11-01 16:15:00"},"preIssue":"853819","preEndTime":"1509524100","now":"1509524317","openTime":"1509524400"}
 *
 * wait open --- current
 * {"Issue":"853824","EndTime":"1509525600","FsEndTime":"1509525548","OpenTime":"2017-11-01 16:40","FsTimeSpace":"52","BonusTime":"2017-11-01 16:41","BonusWeek":"\u661f\u671f\u4e09","IsOpen":"0"}
 *
 */
function get_current_kl8()
{
    global $u1_kl8, $redis, $prev;
    
    do {
        echo "\n获取当期信息…k18…\n";
        $res = curl($u1_kl8, 5000);
        $res = json_decode($res, true);
        
        if(isset($res['IsOpen']) && $res['IsOpen'] == '0'){
            
            $redis->hSet($prev . 'current', 'kl8', $res['Issue']); // 更新当期编号 todo 有下期编号吗？应该在结束下单后，立即更新当期编号为下一期
            
            
            //当期剩余时间
            $secondsLeft = strtotime($res['BonusTime']) - time();
            
            $period = $res['Issue'];
            
            $delay = $secondsLeft * 1000 + 5 * 1000;
            
            swoole_timer_after($delay, function() use ($period){ // 延时执行 todo ?$delay
                get_result_kl8(); // 获取开奖信息
            });
                // 订单处理平仓时间，在开奖时间上，再加若干秒，视情况调整
                $redis->hSet($prev . 'current', 'kl8_openTime', strtotime($res['BonusTime'])); // 设置开奖时间
                
                // 当期停止下单后，修改当期编号，供下单使用
                $nextPeriod = $res['Issue'] + 1; // 下一期
                $next_left = $res['EndTime'] - time();
                swoole_timer_after($next_left * 1000, function() use ($nextPeriod, $redis, $prev){ // 延时执行
                    $redis->hSet($prev . 'current', 'kl8', $nextPeriod); // 更新当期编号
                    $redis->hSet($prev . 'current', 'kl8_openTime', ''); // 清除开奖时间
                });
                    
                    $again = false; // 结束循环
        } else {
            
            // 如果获取失败，延时几秒后重新获取
            sleep(3);
            $again = true;
        }
    } while ($again);
}


/**
 * 生成分析数据
 */
function generate()
{
    while (true) {
        
        $res = yield;
        $last['period'] = $res['period']; // 期号
        $last['s_d'] = isset($last['s_d']) ? $last['s_d'] : 0; //初始化
        
        // 判断末尾单双
        if (preg_match_all("/[0-9]/", $res['num']) == 5) { // 号码包含5位数字
            
            $l = substr(trim($res['num']), -1); // 取最后一位
            if ($l % 2) { // 单
                $s_d = -1;
            } else { // 双
                $s_d = 1;
            }
            
        } else {
            
            $s_d = 0;
        }
        
        // 比较上次，如果同为单，或同为双，计数相加，否则清零
        if ($last['s_d'] < 0 && $s_d < 0 || $last['s_d'] > 0 && $s_d > 0) {
            
            $last['s_d'] += $s_d;
            
        } else {
            
            $last['s_d'] = $s_d;
        }
        
        
        
        
        
        // 输出
        if ($last['s_d']) {
            yield $last;
        }
        
    }
}

/**
 * 生成分析数据
 */
function generate_kl8()
{
    global $redis, $prev;
    
    while (true) {
        
        $res = yield;
        $last['period'] = $res['period']; // 期号
        $last['s_d'] = isset($last['s_d']) ? $last['s_d'] : 0; //初始化
        $last['open_time'] = isset($last['open_time']) ? $last['open_time'] : time(); //初始化
        
        if(strpos($res['num'], '+') !== false){
            $nums = explode(' ', $res['num']);
            $num_win = $nums[2];
            
            if($num_win % 2){// 单
                $s_d = -1;
            }else{
                $s_d = 1;
            }
            
        }else{
            $s_d = 0;
        }
        
        // 比较上次，如果同为单，或同为双，计数相加，否则清零
        if ($last['s_d'] < 0 && $s_d < 0 || $last['s_d'] > 0 && $s_d > 0) {
            
            $last['s_d'] += $s_d;
            
        } else {
            
            $last['s_d'] = $s_d;
        }
        
        if($redis->hExists($prev . 'analyz', 'kl8')){
            $last_issue = $redis->hget($prev . 'analyz', 'kl8');
            if($last_issue){
                $last_issue = json_decode($last_issue, true)[0];
            }
            if(isset($last_issue['period']) && $last_issue['period'] != $last['period'] && $last['s_d']){
                yield $last;
            }
        }else if($last['s_d']){
            yield $last;
        }
    }
}


/**
 * 更新趋势分析缓存
 */
function up_cache($res)
{
    global $redis, $prev;
    
    $t = $redis->hGet($prev . 'analyz', 'ssc'); // 取出缓存数据
    if ($t) {
        
        $t = json_decode($t, true);
        if ($t) {
            
            $tmp = [];
            foreach ($t as $k => $v) {
                // 删除非当天的数据
                $v_day = date('Y') . substr($v['period'], 2, 4);
                if($v_day != date('Ymd') . ''){
                    continue;
                }
                $tmp[] = $v;
            }
            $t = $tmp;
            array_unshift($t, $res); // 将下一条K线合并到之前K线最前面
            
        } else {
            
            $t[] = $res;
        }
    } else {
        
        $t[] = $res;
    }
    $redis->hSet($prev . 'analyz', 'ssc', json_encode($t));
}

/**
 * 更新趋势分析缓存
 */
function up_cache_kl8($res)
{
    global $redis, $prev;
    
    
    if(empty($res)){
        return false;
    }
    
    $t = $redis->hGet($prev . 'analyz', 'kl8'); // 取出缓存数据
    if ($t) {
        
        $t = json_decode($t, true);
        if ($t) {
            
            $tmp = [];
            foreach ($t as $k => $v) {
                // 删除非当天的数据
                $v_day = date('Y-m-d', $v['open_time']);
                if($v_day != date('Y-m-d')){
                    continue;
                }
                $tmp[] = $v;
            }
            $t = $tmp;
            array_unshift($t, $res); // 将下一条K线合并到之前K线最前面
            
        } else {
            
            $t[] = $res;
        }
    } else {
        
        $t[] = $res;
    }
    $redis->hSet($prev . 'analyz', 'kl8', json_encode($t));
}

